package common.db.repository;

import common.db.HashData;
import common.db.io.MyLog;
import common.db.repository.SqlQueryParam;

import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Field;

/**
 * Created by IntelliJ IDEA.
 * User: Paskal
 * Date: Apr 26, 2009
 * Time: 8:43:18 PM
 * To change this template use File | Settings | File Templates.
 */
public class SqlQueryGenerator
{
	private MyLog log = new MyLog(this.getClass());

	private HashMap<String, HashData> cache = new HashMap<String, HashData>();

	private Resourcer mappingResourcer;


	public SqlQueryGenerator(String mapping)
	{
		this.mappingResourcer = new Resourcer(mapping);

	}

	public HashMap<String, HashData> getCache()
	{
		return cache;
	}

	public void setCache(HashMap<String, HashData> cache)
	{
		this.cache = cache;
	}

	public SqlQueryGenerator(Resourcer mappingResourcer)
	{
		this.mappingResourcer = mappingResourcer;
	}

	public void setMappingResourcer(Resourcer mappingResourcer)
	{
		this.mappingResourcer = mappingResourcer;
	}

	public Resourcer getMappingResourcer()
	{
		return mappingResourcer;
	}

	private String createQuery(String table, String kind, List<String> qenericArr,
							   List<String> forSetArr, List<String> conditionArr, String[] fields)
	{
		StringBuilder query = new StringBuilder();
		if (kind.equalsIgnoreCase("insert"))
		{
			query.append("INSERT INTO ");
			query.append(table);
			query.append("(");
			boolean first = true;
			for (String w : forSetArr)
			{
				if (!first)
					query.append(" , ");

				first = false;

				query.append(w);
			}
			query.append(") VALUES( ");
			first = true;
			for (String w : forSetArr)
			{
				if (!first)
					query.append(" , ");

				first = false;

				query.append("?");
			}
			query.append(" )");

		}
		else if (kind.equalsIgnoreCase("editLabel"))
		{
			query.append("UPDATE ");
			query.append(table);
			query.append(" SET ");
			boolean first = true;
			for (String w : forSetArr)
			{
				if (!first)
					query.append(" , ");
				first = false;

				query.append(w);
				query.append(" = ?");
			}
			query.append(" WHERE ");
			first = true;
			for (String w : conditionArr)
			{
				if (!first)
					query.append(" AND ");
				first = false;

				query.append(w);
				query.append(" = ?");
			}
		}
		else if (kind.equalsIgnoreCase("select"))
		{
			query.append("SELECT ");
			boolean first = true;
			for (String w : fields)
			{
				if (!first)
					query.append(" , ");
				first = false;
				query.append(w);
			}
			query.append(" FROM ");
			query.append(table);
			if (!conditionArr.isEmpty())
			{
				query.append(" WHERE ");
				first = true;
				for (String w : conditionArr)
				{
					if (!first)
						query.append(" AND ");
					first = false;

					query.append(w);
					query.append(" = ?");
				}
			}

		}
		else if (kind.equalsIgnoreCase("deleteLabel"))
		{
			query.append("DELETE FROM ");
			query.append(table);
			if (!conditionArr.isEmpty())
			{
				query.append(" WHERE ");

				boolean first = true;
				for (String w : conditionArr)
				{
					if (!first)
						query.append(" AND ");
					first = false;

					query.append(w);
					query.append(" = ?");
				}
			}

		}

		return query.toString();
	}

	public SqlQueryParam getInsert(String mappingTable, Object entity, String... view)
	{
		if (view.length == 0)
			return parametars("insert", mappingTable, entity, "default");
		else
			return parametars("insert", mappingTable, entity, view[0]);
	}

	public SqlQueryParam getUpdate(String mappingTable, Object entity, String... view)
	{
		if (view.length == 0)
			return parametars("editLabel", mappingTable, entity, "default");
		else
			return parametars("editLabel", mappingTable, entity, view[0]);
	}

	public SqlQueryParam getDelete(String mappingTable, Object entity, String... view)
	{
		if (view.length == 0)
			return parametars("deleteLabel", mappingTable, entity, "default");
		else
			return parametars("deleteLabel", mappingTable, entity, view[0]);

	}

	public SqlQueryParam getSelect(String mappingTable, Object entity, String... view)
	{
		if (view.length == 0)
			return parametars("select", mappingTable, entity, "default");
		else
			return parametars("select", mappingTable, entity, view[0]);
	}


	/**
	 * @param kind
	 * @param mappingTable
	 * @param entity
	 * @return Object[]
	 */
	private SqlQueryParam parametars(String kind, String mappingTable, Object entity, String view)
	{

		//Parametar for caching
		String hashKey = kind + mappingTable + view;
		List<String> dbFieldsForInit = null;
		String[] fieldJava = null;
		String[] fieldDB = null;
		List<Object> params = new ArrayList<Object>();
		String query = null;
		List<String> qenericArr = null;
		//-------------------------
		if (!cache.containsKey(hashKey)) // if data NOTcontains in cache, load it
		{
			HashData hd = getDataForHash(kind, mappingTable, view);
			cache.put(hashKey, hd);
		}

		HashData hd = cache.get(hashKey);
		query = hd.getQuery();
		dbFieldsForInit = hd.getFieldsForInit();
		fieldJava = hd.getFieldJava();
		fieldDB = hd.getFieldDB();
		qenericArr = hd.getQenericArr();

		Class clazz = entity.getClass();
		List<Field> fs = new ArrayList<Field>();
		while (clazz != null)
		{
			for (Field f : clazz.getDeclaredFields())
			{
				fs.add(f);
			}
            clazz = clazz.getSuperclass();
		}
        //Field[] fields = clazz.getDeclaredFields();

		for (String forInit : dbFieldsForInit)
		{
			String javaVariableName = findJavaRepresent(forInit, fieldJava, fieldDB);
			for (Field f : fs)//fields)
			{
				String fName = f.getName().toUpperCase();
				System.out.println("+" + fName);
				f.setAccessible(true);
				if (fName.equalsIgnoreCase(javaVariableName))
				{
					try
					{
						Object data = f.get(entity);
						params.add(data);
//						System.out.println(fName+ "=" + data + " !!!");
					}
					catch (IllegalAccessException e)
					{
						e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
					}
				}
			}

		}


		return new SqlQueryParam(query, params.toArray(), qenericArr);
	}

	private List<String> union(String kind, List<String> forSetArr, List<String> conditionArr)
	{
		List<String> union = null;
		if (kind.equalsIgnoreCase("insert"))
		{
			return forSetArr;
		}
		else if (kind.equalsIgnoreCase("select"))
		{
			return conditionArr;
		}
		else if (kind.equalsIgnoreCase("deleteLabel"))
		{
			return conditionArr;
		}
		else
		{
			union = new ArrayList<String>(forSetArr);
			union.addAll(conditionArr);
			return union;
		}

	}

	private String findJavaRepresent(String forInit, String[] fieldJava, String[] fieldDB)
	{
		forInit = forInit.trim();
		for (int i = 0; i < fieldDB.length; i++)
		{
			if (forInit.equalsIgnoreCase(fieldDB[i]))
				return fieldJava[i];
		}
		return null;
	}

	public String getSelectFrom(String mappingTable, String view)
	{
		String hashKey = "select" + mappingTable + view;

		if (!cache.containsKey(hashKey)) // if data NOTcontains in cache, load it
		{
			HashData hd = getDataForHash("select", mappingTable,
					view);// new HashData(query, dbFieldsForInit, fieldJava, fieldDB, qenericArr);
			cache.put(hashKey, hd);
		}
		HashData hd = cache.get(hashKey);

		return hd.getQuery();
	}

	public String getDeleteFrom(String mappingTable, String view)
	{
		String hashKey = "deleteLabel" + mappingTable + view;

		if (!cache.containsKey(hashKey)) // if data NOTcontains in cache, load it
		{
			HashData hd = getDataForHash("deleteLabel", mappingTable,
					view);// new HashData(query, dbFieldsForInit, fieldJava, fieldDB, qenericArr);
			cache.put(hashKey, hd);
		}
		HashData hd = cache.get(hashKey);

		return hd.getQuery();
	}

	private HashData getDataForHash(String kind, String mappingTable, String view)
	{
		String mapping = mappingResourcer.query(mappingTable);
		//---------------------------------------
		String[] section = mapping.split("###");
		String table = section[0].trim();
		String declarationPart = section[1];
		String viewPart = section[2];

		List<String> dbFieldsForInit = null;
		String[] fieldJava = null;
		String[] fieldDB = null;
		String query = null;
		List<String> qenericArr = null;

		int indexViewBeg = section[2].indexOf(view) + view.length();
		int indexViewEnd = section[2].indexOf(";", indexViewBeg);
		String[] viewVars = viewPart.substring(indexViewBeg, indexViewEnd).split(":");

		qenericArr = new ArrayList<String>();
		List<String> forSetArr = new ArrayList<String>();
		List<String> conditionArr = new ArrayList<String>();


		String generic = viewVars[0];
		String forSet = viewVars[1];
		String condition = viewVars[2];

		for (String var : generic.split(","))
		{
			var = var.trim();
			if (!var.isEmpty())
				qenericArr.add(var);
		}


		for (String var : forSet.split(","))
		{
			var = var.trim();
			if (!var.isEmpty())
				forSetArr.add(var);
		}


		for (String var : condition.split(","))
		{
			var = var.trim();
			if (!var.isEmpty())
				conditionArr.add(var);
		}


		declarationPart = declarationPart.trim();

		String[] rows = declarationPart.split(";");
		fieldJava = new String[rows.length];
		fieldDB = new String[rows.length];

		for (int i = 0; i < rows.length; i++)
		{

			fieldJava[i] = rows[i].split("=")[0].trim();
			fieldDB[i] = rows[i].split("=")[1].trim();

		}

		query = createQuery(table, kind, qenericArr,
				forSetArr, conditionArr, fieldDB);
		//System.out.println("Query:" + query);

		dbFieldsForInit = union(kind, forSetArr, conditionArr);
		if (dbFieldsForInit == null)
			throw new RuntimeException("query initiailsation field are empty");

		return new HashData(query, dbFieldsForInit, fieldJava, fieldDB, qenericArr);
	}

	public String insertIntegerParamsCondition(String sql, List<Integer> ids)
	{
		StringBuilder sb = new StringBuilder();
		if (ids.isEmpty())
			sb.append("NULL");
		else
		{
			boolean first = true;
			for(Integer i : ids)
			{
				if(!first) sb.append(",");
				sb.append(i);
				first = false;

			}
		}
		return sql.replaceFirst("###", sb.toString());
	}
}
